﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WeddingEventManagement_API
{
    public class InvoiceManager : IInvoiceManager
    {
        #region Invoices

        #region Read

        public List<Invoice> GetInvoices(int clientID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                List<int> eventIDs =
                    (from e in db.Events.Where(i => i.ClientID == clientID)
                    select e.EventID).ToList();

                return db.Invoices.Include("InvoiceItems").Where(i => eventIDs.Contains(i.EventID)).ToList();
            }
        }

        public Invoice GetInvoice(int invoiceID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                return db.Invoices
                    .Include("InvoiceItems")
                    .Include("Event")
                    .Include("Event.Client")
                    .Include("Event.EventType")
                    .FirstOrDefault(i => i.InvoiceID == invoiceID);
            }
        }

        public decimal GetInvoiceTotal(int invoiceID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                decimal total = 0.00M;
                var invoice = db.Invoices.SingleOrDefault(i => i.InvoiceID == invoiceID);

                // Add Invoice Items
                foreach (var item in invoice.InvoiceItems)
                {
                    total += (item.Qty * item.Price);
                }

                // Add Delivery Charge
                total += invoice.InvDeliveryCharge;

                // Apply Discount
                if (invoice.InvDiscountPercentage != 0)
                {
                    total -= (total * (invoice.InvDiscountPercentage / 100));
                }

                // Apply Taxes
                total += (total * (invoice.InvTaxRate / 100));

                return total;
            }
        }

        public decimal GetRemainingBalanceForInvoice(int invoiceID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                decimal total = this.GetInvoiceTotal(invoiceID);

                //Subtract payments from total
                IEnumerable<Payment> payments = db.Payments.Where(i => i.InvoiceID == invoiceID);
                foreach (Payment p in payments)
                {
                    total -= p.Amount;
                }

                return total;
            }
        }

        public double GetRemainingBalanceForClient()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Create, Update, Delete

        public int CreateInvoice(int eventID, Guid creatorUserID, DateTime dueDate, List<InvoiceItem> invoiceItems = null, string notes = null)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                Guid invoiceUID = Guid.NewGuid();

                //Create Invoice
                Invoice invoice = new Invoice
                {
                    EventID = eventID,
                    CreatorUserID = creatorUserID,
                    DueDate = dueDate,
                    CreatedDate = DateTime.UtcNow,
                    Notes = notes,
                    InvoiceUID = invoiceUID,
                    IsEditable = true,
                    InvTaxRate = Decimal.Parse(new SettingsManager().GetSetting("_appTaxRate").value),
                    InvDeliveryCharge = Decimal.Parse(new SettingsManager().GetSetting("_appDeliveryCharge").value),
                    InvDiscountPercentage = 0,
                };
                db.Invoices.Add(invoice);
                db.SaveChanges();

                //Retrieve Created Invoice
                Invoice newInvoice = db.Invoices.FirstOrDefault(i => i.InvoiceUID == invoiceUID);

                //Add Items
                if (invoiceItems != null)
                {
                    foreach (InvoiceItem item in invoiceItems)
                    {
                        item.InvoiceID = newInvoice.InvoiceID;
                        db.InvoiceItems.Add(item);
                    }
                    db.SaveChanges();
                }
                else //Add blank item if item array is empty
                {
                    db.InvoiceItems.Add(new InvoiceItem() { InvoiceID = newInvoice.InvoiceID, Description = "", Name = "", Price = 0, Qty = 1 });
                    db.SaveChanges();
                }

                return newInvoice.InvoiceID;
            }
        }

        #endregion

        #endregion

        #region Payments

        #region Read

        public int GetInvoiceIDForPayment(int paymentID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                Payment p = db.Payments.FirstOrDefault(i => i.PaymentID == paymentID);
                return p.InvoiceID;
            }
        }

        public List<Payment> GetPayments(int? invoiceID = null)
        {
            using(CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                if (invoiceID.HasValue)
                {
                    return db.Payments.Where(i => i.InvoiceID == invoiceID.Value).ToList();
                }
                else
                {
                    return db.Payments.ToList();
                }
            }
        }

        public List<string> GetPaymentTypes()
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                return db.Payments.Select(p => p.PaymentType).Distinct().ToList();
            }
        }

        #endregion

        #region Create, Update, Delete

        public void DeletePayment(int paymentID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                Payment p = db.Payments.FirstOrDefault(i => i.PaymentID == paymentID);
                db.Payments.Remove(p);
                db.SaveChanges();
            }
        }

        public int AddPayment(int invoiceID, decimal amount, string paymentType, Guid creatorUserID, DateTime? paymentTime = null)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                Guid paymentUID = Guid.NewGuid();
                DateTime time = paymentTime.HasValue ? paymentTime.Value : DateTime.UtcNow;
                Payment p = new Payment 
                {
                    Amount = amount,
                    CreatorUserID = creatorUserID,
                    DateTime = time,
                    InvoiceID = invoiceID,
                    PaymentType = paymentType,
                    PaymentUID = paymentUID
                };
                db.Payments.Add(p);
                db.SaveChanges();

                return db.Payments.FirstOrDefault(i => i.PaymentUID == paymentUID).PaymentID;
            }
        }

        #endregion

        #endregion

        public void SaveInvoice(Invoice model)
        {
            if (model.InvoiceID != 0)
            {
                using (CreativeSystemsEntities db = new CreativeSystemsEntities())
                {
                    var databaseInvoice = db.Invoices.Where(i => i.InvoiceID == model.InvoiceID).SingleOrDefault();
                    databaseInvoice.InvDiscountPercentage = model.InvDiscountPercentage;
                    databaseInvoice.InvDeliveryCharge = model.InvDeliveryCharge;
                    databaseInvoice.InvTaxRate = model.InvTaxRate;
                    databaseInvoice.UpdateUserID = model.UpdateUserID;
                    databaseInvoice.Notes = model.Notes;
                    
                    //Update InvoiceItems (Delete/Update)
                    var invoiceItemsInDB = databaseInvoice.InvoiceItems.ToList();
                    foreach (var invoiceItemInDB in invoiceItemsInDB )
                    {
                        var invoiceItem = model.InvoiceItems
                            .SingleOrDefault(i => i.InvoiceItemID == invoiceItemInDB.InvoiceItemID);

                        if (invoiceItem != null)
                        {
                            if (invoiceItem.InvoiceItemID != -1)
                            {
                                // Update it!
                                invoiceItem.InvoiceID = model.InvoiceID;
                                db.Entry(invoiceItemInDB).CurrentValues.SetValues(invoiceItem);
                            }
                            else
                            {
                                db.InvoiceItems.Remove(invoiceItemInDB);
                            }
                        }
                        else
                        {
                            //Delete it!
                            db.InvoiceItems.Remove(invoiceItemInDB);
                        }
                    }

                    //Add new InvoiceItems
                    foreach (var item in model.InvoiceItems)
                    {
                        //Is the Item not in the DB?
                        if (!invoiceItemsInDB.Any(i => i.InvoiceItemID == item.InvoiceItemID))
                        {
                            if (item.InvoiceItemID != -1)
                            {
                                item.InvoiceID = model.InvoiceID;

                                item.Name = item.Name ?? "";
                                item.Description = item.Description ?? "";

                                //Add it!
                                db.InvoiceItems.Add(item);
                            }
                        }
                    }

                    db.SaveChanges();
                }
            }
        }

        
    }
}
